package com.zb.zeus.gateway.config;

import com.zb.zeus.common.core.utils.StringUtils;
import com.zb.zeus.common.core.web.domain.AjaxResult;
import com.zb.zeus.gateway.handler.ValidateCodeHandler;
import com.zb.zeus.gateway.limiter.ZeusRequestRateLimiterGatewayFilterFactory;
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.timelimiter.TimeLimiterConfig;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.gateway.config.GatewayProperties;
import org.springframework.cloud.gateway.discovery.DiscoveryLocatorProperties;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RequestPredicates;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

import java.time.Duration;
import java.util.List;
import java.util.Map;

import static org.springframework.cloud.gateway.support.NameUtils.normalizeFilterFactoryName;

/**
 * 网关一些默认的统一配置
 *
 * @author fujianjian
 * @date 2023/4/12 20:14
 **/
@Configuration
@RequiredArgsConstructor
public class ZeusGatewayUnifyConfiguration {

    private final ValidateCodeHandler validateCodeHandler;

    @SuppressWarnings("rawtypes")
    @Bean
    public RouterFunction codeRouterFunction() {
        return RouterFunctions.route(
                RequestPredicates.GET("/code")
                        .and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                validateCodeHandler);
    }

    @Bean
    public RouterFunction<ServerResponse> defaultFallbackFunction() {
        return RouterFunctions.route(
                RequestPredicates.GET("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
                request -> ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
                        .body(BodyInserters.fromValue(
                                AjaxResult.error(5003, "当前系统负载过高,请稍后重试"))));
    }


    @Bean
    public ReactiveResilience4JCircuitBreakerFactory reactiveResilience4JCircuitBreakerFactory(
            CircuitBreakerRegistry circuitBreakerRegistry,
            TimeLimiterRegistry limiterRegistry) {
        ReactiveResilience4JCircuitBreakerFactory factory = new ReactiveResilience4JCircuitBreakerFactory(
                circuitBreakerRegistry, limiterRegistry);

        factory.configureDefault(id-> new Resilience4JConfigBuilder(id)
                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(2L)).build())
                .build());
        return factory;


    }

    @RequiredArgsConstructor
    @Configuration(proxyBeanMethods = false)
    // @AutoConfigureAfter({GatewayDiscoveryClientAutoConfiguration.class, ZeusGatewayPropertyEnhanceConfiguration.class})
    @ConditionalOnProperty(value = "spring.cloud.gateway.discovery.locator.enabled", havingValue = "true")
    public static class ZeusDiscoveryClientConfiguration implements SmartInitializingSingleton {

        private final DiscoveryLocatorProperties properties;
        private final FilterDefinition zeusRequestRateLimiterFilter;


        @Override
        public void afterSingletonsInstantiated() {
            properties.getFilters().add(zeusRequestRateLimiterFilter);
        }
    }

    @RequiredArgsConstructor
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnProperty(name = "spring.cloud.gateway.enabled", matchIfMissing = true)
    public static class ZeusGatewayPropertyEnhanceConfiguration implements SmartInitializingSingleton {
        private final GatewayProperties properties;

        @Bean
        public FilterDefinition zeusRequestRateLimiterFilter() {
            FilterDefinition filter = new FilterDefinition();
            filter.setName(normalizeFilterFactoryName(ZeusRequestRateLimiterGatewayFilterFactory.class));
            filter.addArg(RedisRateLimiter.CONFIGURATION_PROPERTY_NAME + ".replenish-rate", "1");
            filter.addArg(RedisRateLimiter.CONFIGURATION_PROPERTY_NAME + ".burst-capacity", "3");
            // filter.addArg("key-resolver","\"#{@hostAddrKeyResolver}\"");
            return filter;
        }

        @Override
        public void afterSingletonsInstantiated() {
            FilterDefinition requestRateLimiterFilter = zeusRequestRateLimiterFilter();
            for (RouteDefinition route : properties.getRoutes()) {
                addFilterDefinitionIfAbsent(route.getFilters(), requestRateLimiterFilter);
            }
        }

        private void addFilterDefinitionIfAbsent(List<FilterDefinition> filterDefinitionList, FilterDefinition targetFilterDefinition) {
            for (FilterDefinition definition : filterDefinitionList) {
                if (StringUtils.equals(definition.getName(), targetFilterDefinition.getName())) {
                    Map<String, String> args = definition.getArgs();
                    if (MapUtils.isEmpty(args)) {
                        definition.setArgs(targetFilterDefinition.getArgs());
                    }
                    return;
                }
            }
            filterDefinitionList.add(targetFilterDefinition);
        }
    }

}
